#!/usr/local/bin/python2.7
# Converts 320x200 1bpp bitmaps into a C64 machine-code program.
#
# How I generate the proper output with cc65:
# ./image2prg.py image.png|ca65 -o a.o /dev/stdin && ld65 -t none a.o -o pic.prg
# Glorious Unix Master Race. Dirty Windows peasants.
#
# Use the F1/F3/F5/F7 key to change colours.
# --
# Chris Baird <cjb@brushtail.apana.org.au> did this. May 2017.
# Licenced according to the GPLv3

import sys
import binascii
from PIL import Image, ImageFilter

dobinaryoutput = 1

######################################################################
# I went a bit silly with the self-modifing code thing...

driver = """
        .org 2047
	.word 2049
        .word 2059
        .word 2017
        .byte 158,"2061"
        .byte 0,0,0

        value = value0+1
        lengthlo = length0+1
        lengthhi = length1+1

	lda #1
	sta 56576
	lda #56
	sta 53272
	lda #59
	sta 53265
	jsr colouring
	sty SP+1
	lda #>40960
	sta SP+2
	lda #<DATA
	sta DP+1
	lda #>DATA
	sta DP+2

mainloop:
	ldy #0
        sty lengthlo
        sty lengthhi
	sty value

calc:   lda lengthlo
	sta lengthhi
value0:	lda #0
	sta lengthlo
	sty value
	lsr lengthhi
	ror lengthlo
	ror value

DP:	lda $FFFF
	php
	and #127
	adc value
	sta value
	inc DP+1
	bne br1
	inc DP+2
br1:	plp
	bpl calc

fill:	lda value
SP:	sta $FFFF
	clc
	lda SP+1
	adc #1
	sta SP+1
	lda SP+2
	adc #0
	sta SP+2
	cmp #>48960
	bne fill1
	lda SP+1
	cmp #<48960
	beq twiddle

fill1:	sec
length0: lda #0
	sbc #1
	sta lengthlo
length1: lda #0
	sbc #0
	sta lengthhi
	cmp #255
	bne fill
	beq mainloop

twiddle:
        lda #0
        sta background
        sta foreground
        jsr $FFE4
        cmp #32
        beq reset
        cmp #13
        beq reset
        cmp #133 ; F1
        beq incback
        cmp #134 ; F3
        beq decback
        cmp #135 ; F5
        beq incfore
        cmp #136 ; F7
        beq decfore
        bne twiddle

        background = back0+1
        foreground = fore0+1

incback: lda #1
        .byte $2C
decback: lda #15
        sta background
        bne change

incfore: lda #16
        .byte $2C
decfore: lda #240
        sta foreground

change: clc
        lda colours
back0:  adc #0
        and #15
        sta background
        clc
        lda colours
fore0:  adc #0
        and #240
        ora background
        sta colours
        jsr colouring
        jmp twiddle

        colours = colour0+1

colouring:
	ldy #0
colour0: lda #16
sloop:	sta $8C00,y
	sta $8D00,y
	sta $8E00,y
	sta $8EE8,y
	iny
	bne sloop
        and #15
        sta 53280
        sta 53281
	rts

reset:  jmp 64738

DATA:"""

driverhex = "01080b08e1079e32303631000000a9018d00dda9388d18" \
            "d0a93b8d11d020f7088c6e08a9a08d6f08a9168d5408a9" \
            "098d5508a0008c8e088c95088c4308ad8e088d9508a900" \
            "8d8e088c43084e95086e8e086e4308adac1108297f6d43" \
            "088d4308ee5408d003ee55082810d2ad43088d40bf18ad" \
            "6e0869018d6e08ad6f0869008d6f08c9bfd007ad6e08c9" \
            "40f01538a900e9018d8e08a900e9008d9508c9ffd0cbf0" \
            "90a9008ddd088de80820e4ffc920f063c90df05fc985f0" \
            "0ec986f00dc987f010c988f00fd0dba9012ca90f8ddd08" \
            "d008a9102ca9f08de80818adfa086900290f8ddd0818ad" \
            "fa08690029f00ddd088dfa0820f7084ca108a000a91099" \
            "008c99008d99008e99e88ec8d0f1290f8d20d08d21d060" \
            "4ce2fc"

driverbin = binascii.unhexlify(driverhex)

######################################################################

pixels = ( 128, 64, 32, 16, 8, 4, 2, 1 )
xtab = ( 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96,\
         104, 112, 120, 128, 136, 144, 152, 160, 168, 176,\
         184, 192, 200, 208, 216, 224, 232, 240, 248, 256,\
         264, 272, 280, 288, 296, 304, 312 )
mem = [0] * 8000

def c64hiresplot (x, y):
  yt = y >> 3;
  plot_addr = (yt << 8) + (yt << 6) + (xtab[x >> 3]) + (y & 7)
  mem[plot_addr] = mem[plot_addr] | pixels[x & 7];

######################################################################

im = Image.open(sys.argv[1]).convert('RGB')
if im.size != (320,200):
  print "image not 320x200-- exiting"
  sys.exit()

for y in range(0,200):
  for x in range(0,320):
    if im.getpixel((x,y))[0]:
      c64hiresplot(x,y)

p = 0
l = 0
lb = -1
out = ""

for p in range(0,8000):
  b = mem[p]
  if (b == lb) & (p < 7999):
    l = l + 1
  else:
    if p < 7999:
      l = l - 1
    if p > 0:
      s = (l*256) + lb
      res = ""
      while not res or s > 0:
        s, i = divmod(s, 128)
        res = chr(i) + res
      c = ord(res[-1:])
      out = out + res[:-1] + chr(c+128)
    l = 1
    lb = b

######################################################################

if dobinaryoutput:
  if len(sys.argv) > 2:
    outfile = sys.argv[2]
  else:
    outfile = "output.prg"
  f = open(outfile, "w")
  f.write(driverbin)
  f.write(out);
  f.close()
else:
  print driver
  tb = 0
  for i in range(0,len(out)):
    if tb == 0:
      line = ".byte "
    c = str(ord(out[i:i+1]))
    line = line + str(c)
    tb = tb + 1
    if (i == len(out)-1) | (tb == 32):
      print line
      tb = 0
    else:
      line = line + ","

#
